package com.ts.api.module.channel.service.impl;

import com.gitee.apanlh.util.algorithm.digest.MD5;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import com.ts.api.common.constant.BizEnum;
import com.ts.api.common.constant.channel.ChannelIsRegisterEnum;
import com.ts.api.common.constant.channel.ChannelUserStatus;
import com.ts.api.common.constant.common.SwitchEnum;
import com.ts.api.common.context.ChannelContext;
import com.ts.api.common.context.ChannelRequestContext;
import com.ts.api.common.localcache.DistrictCache;
import com.ts.api.exp.BizException;
import com.ts.api.module.channel.dao.ChannelUserDao;
import com.ts.api.module.channel.entity.ChannelRequest;
import com.ts.api.module.channel.entity.bo.ChannelRegisterFieldBO;
import com.ts.api.module.channel.entity.bo.ChannelUserBO;
import com.ts.api.module.channel.entity.po.ChannelFlowConfigPO;
import com.ts.api.module.channel.entity.po.ChannelUserRegisterPO;
import com.ts.api.module.channel.entity.qo.ChannelUserQO;
import com.ts.api.module.channel.service.ChannelConfigService;
import com.ts.api.module.channel.service.ChannelUserService;
import com.ts.api.module.channel.service.DoubleWriteUserService;
import com.ts.api.module.common.entity.DistrictPO;
import com.ts.api.module.common.service.SmsService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class ChannelUserServiceImpl implements ChannelUserService {

    @Autowired
    private ChannelUserDao channelUserDao;
    @Autowired
    private ChannelConfigService channelConfigService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private DoubleWriteUserService doubleWriteUserService;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional(readOnly = true)
    public ChannelUserRegisterPO get(ChannelUserQO channelUserQO) {
        return channelUserDao.get(channelUserQO);
    }

    @Override
    @Transactional(readOnly = true)
    public List<ChannelUserRegisterPO> listRightMatchPhone(ChannelUserQO channelUserQO) {
        return channelUserDao.listRightMatchPhone(channelUserQO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChannelUserBO register(String smsCode, ChannelRegisterFieldBO registerFieldBO, boolean isThrows) {
        ChannelRequest channelRequest = ChannelRequestContext.get();

        ChannelUserRegisterPO userRegister = new ChannelUserRegisterPO();
        registerFieldBO.dynamicSetField(userRegister);

        //  验证渠道是否开启短信验证码
        ChannelFlowConfigPO channelFlowConfigPO = channelConfigService.getFlowConfigByChannelId();
        if (SwitchEnum.hasEnable(channelFlowConfigPO.getEnableSms())) {
            smsService.validSmsCode(userRegister.getPhone(), smsCode);
        }

        //  是否用户已存在
        RLock lock = redissonClient.getLock("register:" + userRegister.getPhone());
        lock.lock(10, TimeUnit.SECONDS);
        try {
            ChannelUserQO userQO = ChannelUserQO.createPhone(userRegister.getPhone());
            ChannelUserRegisterPO cancelUserRegisterPO = channelUserDao.get(userQO);
            //  如果用户已存在直接返回
            if (ValidParam.isNotNull(cancelUserRegisterPO)) {
                ChannelUserRegisterPO update = new ChannelUserRegisterPO();
                update.setId(cancelUserRegisterPO.getId());

                //  是否存在注销->修改普通用户后重新注册
                boolean hasCancel = ChannelUserStatus.CANCEL.getStatus().equals(cancelUserRegisterPO.getStatus());
                if (hasCancel) {
                    update.setStatus(ChannelUserStatus.NORMAL.getStatus());
                }

                //  修改用户ip以及老用户标识
                update.setIp(channelRequest.getIp());
                update.setBrowserName(channelRequest.getUserAgent().getBrowser().getName());
                update.setOsName(channelRequest.getUserAgent().getOperatingSystem().getName());
                update.setDeviceType(channelRequest.getUserAgent().getOperatingSystem().getDeviceType().getName());
                update.setIsChannelRegister(ChannelIsRegisterEnum.OLD.getType());
                channelUserDao.update(update);
                return new ChannelUserBO(cancelUserRegisterPO.getId(), cancelUserRegisterPO.getPhone());
            }

            //  查询地区编码
            if (ValidParam.isNotEmpty(userRegister.getAreaCode())) {
                DistrictPO districtPO = DistrictCache.getByCode(userRegister.getAreaCode());
                userRegister.setAreaName(districtPO.getName());
            }

            userRegister.setLoanChannelId(ChannelContext.getId());
            userRegister.setIp(channelRequest.getIp());
            userRegister.setIsChannelRegister(ChannelIsRegisterEnum.NEW.getType());
            userRegister.setPhoneMd5(MD5.create().digestToHex(userRegister.getPhone()));
            userRegister.setBrowserName(channelRequest.getUserAgent().getBrowser().getName());
            userRegister.setOsName(channelRequest.getUserAgent().getOperatingSystem().getName());
            userRegister.setDeviceType(channelRequest.getUserAgent().getOperatingSystem().getDeviceType().getName());
            userRegister.setCreateTime(DateUtils.currentTime());
        } finally {
            lock.unlock();
        }

        try {
            doubleWriteUserService.insertUser(userRegister);
        } catch (DuplicateKeyException e) {
            //  并发处理避免获取不到id
            ChannelUserQO userQO = ChannelUserQO.createPhone(userRegister.getPhone());
            userRegister.setId(channelUserDao.get(userQO).getId());
            //  H5目前使用
            if (isThrows) {
                BizException.fastFail(BizEnum.REGISTER_DUPLICATE);
            }
        }
        return new ChannelUserBO(userRegister.getId(), userRegister.getPhone());
    }

    @Override
    public ChannelUserRegisterPO insert(ChannelUserRegisterPO channelUserRegisterPO) {
        channelUserDao.insert(channelUserRegisterPO);
        return channelUserRegisterPO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(ChannelUserRegisterPO userRegisterPO) {
        return channelUserDao.update(userRegisterPO);
    }

    @Override
    @Transactional(readOnly = true)
    public int countByPhoneMd5(String phoneMd5) {
        return channelUserDao.countByPhoneMd5(phoneMd5);
    }

    @Override
    @Transactional(readOnly = true)
    public boolean hasLocalExist(String phoneMd5) {
        int row = channelUserDao.countByPhoneMd5(phoneMd5);
        return row > 0;
    }
}
